Factory Pattern
⚙️ 3️⃣ Factory Pattern — Creating platform-specific handlers
Centralize creation of objects (like API handlers or executors) based on configuration or environment.
---
🧩 Example — Trading platform executor factory
public interface ITradeExecutor
{
void Execute(Order order);
}
public class Mt4Executor : ITradeExecutor
{
public void Execute(Order order) =>
Console.WriteLine($"[MT4] Executing {order.Symbol}");
}
public class Mt5Executor : ITradeExecutor
{
public void Execute(Order order) =>
Console.WriteLine($"[MT5] Executing {order.Symbol}");
}
public static class ExecutorFactory
{
public static ITradeExecutor Create(string platform) => platform switch
{
"MT4" => new Mt4Executor(),
"MT5" => new Mt5Executor(),
_ => throw new ArgumentException("Unknown platform")
};
}
// --- Usage ---
var platform = "MT5";
var executor = ExecutorFactory.Create(platform);
executor.Execute(new Order { Symbol = "USDJPY", Amount = 5000 });
✅ Why it matters:
- Simplifies platform switching (MT4, MT5, FIX, cTrader).
- New platforms require no refactor — just a new class and switch entry.
---
Questions & Answers
Q: When should you use a Factory pattern in .NET services?
A: When object creation depends on runtime context (config, tenant, instrument) and you want to isolate creation logic from consumers. Factories prevent scattered new calls and keep construction consistent.
Q: How does DI interact with factories?
A: You can register implementations in DI and inject Func<Key, ITradeExecutor> or an IExecutorFactory that resolves services by key. This keeps factories composable with scoped dependencies.
Q: How do you avoid giant switch statements as platforms grow?
A: Use a dictionary of delegates, reflection-based registration, or DI IServiceProvider lookups keyed by platform. Alternatively, combine with the Strategy pattern so each executor registers itself.
Q: What’s the benefit of abstract factories?
A: When you need to create families of related objects (e.g., executor + validator + serializer per platform), an abstract factory ensures compatible components are produced together.
Q: How do factories aid testing?
A: Tests can inject fake factories returning mock executors, isolating code under test without hitting real integrations. It also simplifies verifying that the correct executor is chosen for a scenario.
Q: How do you handle configuration-driven factories?
A: Load mappings from configuration or feature flags, then let the factory instantiate types via DI. This enables runtime toggles (e.g., route VIP tenants to a premium executor) without code changes.
Q: When is the factory pattern overkill?
A: When only two concrete types exist and creation logic is trivial. Start simple, and introduce a factory once switching logic repeats or needs shared validation/logging.
Q: How do you ensure factories remain SRP-compliant?
A: Keep them focused on creation. Any orchestration, validation, or logging should be delegated to other components or decorators so factories don't become god objects.
Q: Can factories return async results?
A: Yes—define methods returning Task<T> if creation involves I/O (e.g., pulling secrets). Just ensure callers understand the lifecycle and avoid blocking .Result.
Q: How do you register factories in DI?
A: Register each concrete type and a factory delegate: services.AddTransient<ExecutorFactory>(); or services.AddSingleton<Func<string, ITradeExecutor>>(provider => key => provider.GetRequiredKeyedService<ITradeExecutor>(key));.